package com.go8.mall.controller;

import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.go8.mall.config.AlipayConfig;
import com.go8.trade.pojo.Order;
import com.go8.trade.pojo.OrderPay;
import com.go8.trade.service.OrderService;

@Controller
public class AliPayController {
	@Autowired private OrderService orderService;

	@Autowired private AlipayConfig alipayConfig;

	/**
	 * 前往支付宝第三方网关进行支付
	 * 
	 * @param orderId
	 * @return
	 */
	@GetMapping(value = "/alipay", produces = "text/html; charset=UTF-8")
	public @ResponseBody String doPay(long orderId) {
		Order order = orderService.get(orderId);
		// 返回支付页面内容
		return zfb(order);
	}

	/**
	 * 支付宝异步通知回调的接口功能
	 * 该页面不能在本机电脑测试，请到服务器上做测试。请确保外部可以访问该接口。
	 * 如果没有收到该接口返回的 success
	 * 建议该接口只做支付成功的业务逻辑处理，退款的处理请以调用退款查询接口的结果为准。
	 * 异步的，第一次收到订单信息（以下都称之为“通知”）是与返回页近乎等同或等同的同步时间，
	 * 在判断不成功的情况下，会收到第二次第三次等次数的通知，时间间隔从最先的一两分钟，
	 * 到后面的几个小时。失效时间是48小时。
	 * 注意是post方法 分布式事务：最大努力通知方案。
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@PostMapping("/alipay/notify_url")
	public @ResponseBody String aliNotifyUrl(HttpServletRequest request, HttpServletRequest response) throws Exception {
		// 获取支付宝POST过来反馈信息
		Map<String, String> params = new HashMap<String, String>();
		Map<String, String[]> requestParams = request.getParameterMap();
		for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
			}
			// 乱码解决，这段代码在出现乱码时使用
			valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
			params.put(name, valueStr);
		}

		boolean signVerified = AlipaySignature.rsaCheckV2(params, AlipayConfig.alipay_public_key, AlipayConfig.charset,
				AlipayConfig.sign_type); // 调用SDK验证签名

		// ——请在这里编写您的程序（以下代码仅作参考）——

		/*
		 * 实际验证过程建议商户务必添加以下校验： 1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
		 * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额）， 3、校验通知中的seller_id（或者seller_email)
		 * 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email）
		 * 4、验证app_id是否为该商户本身。
		 */
		if (signVerified) {// 验证成功
			// 商户订单号
			String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");

			// 支付宝交易号
			String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");

			// 交易状态
			String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

			// 付款金额
			String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");

			if (trade_status.equals("TRADE_FINISHED")) {
				// 判断该笔订单是否在商户网站中已经做过处理
				// 如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
				// 如果有做过处理，不执行商户的业务程序

				// 注意： 尚自习的订单没有退款功能, 这个条件判断是进不来的, 所以此处不必写代码
				// 退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知
			} else if (trade_status.equals("TRADE_SUCCESS")) {
				// 判断该笔订单是否在商户网站中已经做过处理
				// 如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
				// 如果有做过处理，不执行商户的业务程序

				// 注意：
				// 付款完成后，支付宝系统发送该交易状态通知

				// 修改订单状态，改为 支付成功，已付款; 同时新增支付流水
				OrderPay orderpay = new OrderPay();
				orderpay.setOrderId(new Long(out_trade_no));
				orderpay.setPayNo(trade_no);
				orderpay.setPayType("zfb");
				orderpay.setPayStatus(trade_status);
				orderService.updateOrderStatus(orderpay);

				System.out.println("TRADE_SUCCESS...");
			}
			return "success";
		} else {// 验证失败
			System.out.println("支付, 验签失败...");
			return "fail";
		}
	}



	/**
	 * 支付宝同步返回页面，展示给买家看的页面，一般不做业务处理
	 * 使用的get请求
	 * 这里做业务处理的原因是，上面的异步通知功能，需要部署到服务器上，
	 * 提供外网访问，才能被支付宝服务器所调用，所以测试不了，
	 * 这里把业务代码写在这里测试一下。
	 * 另外如果线上业务没有开启异步通知功能，那业务代码也就只能写在这个回调页面里了，
	 * 不过这样做的话，会出现掉单的问题：在支付宝支付成功，回调页面之前，用户就关闭了浏览器，
	 * 导致该页面没有执行。
	 *  大家这里存在一个疑问，一般大家的做法都是把数据库更新些在返回页中，
	 *  但是很多情况下出现了订单不同步即掉单现象。这是为什么？
	 答：返回页是当前页面自动跳转的，这虽然跳转的反应速度不错，
	 但人的手动关闭该页面操作绝对可以使之在没有跳转回来之前就关掉了该页面，
	 此时原本该数据库更新的程序并没有被启动，这样直接导致了掉单，所以一般大商
	 户，尤其是网络游戏行业的即时到帐充值的技术做法是：返回页中有订单处理程序，
	 通知页中也有，当返回页中的订单没做过处理时，通知页中的数据处理程序便启动；
	 这样即可近乎100%解决掉单问题。
	 * @param request
	 * @param model
	 * @return
	 * @throws Exception
	 */
	@GetMapping("/alipay/return_url")
	public String aliReturnUrl(HttpServletRequest request, Model model) throws Exception {
		// 获取支付宝GET过来反馈信息
		Map<String, String> params = new HashMap<String, String>();
		Map<String, String[]> requestParams = request.getParameterMap();
		for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
			}
			// 乱码解决，这段代码在出现乱码时使用
			valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
			params.put(name, valueStr);
		}

		//注意接口版本rsaCheckV2
		boolean signVerified = AlipaySignature.rsaCheckV2(params, AlipayConfig.alipay_public_key, AlipayConfig.charset,
				AlipayConfig.sign_type); // 调用SDK验证签名

		// ——请在这里编写您的程序（以下代码仅作参考）——
		if (true) {
			// 商户订单号
			String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");

			// 支付宝交易号
			String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");

			// 交易状态
			//String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
			
			// 付款金额
			String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
			
			//timestamp 支付时间格式    2020-05-13 20:34:36
			String time = new String(request.getParameter("timestamp").getBytes("ISO-8859-1"), "UTF-8");

			// 修改订单状态，改为 支付成功，已付款; 同时新增支付流水
			OrderPay orderpay = new OrderPay();
			orderpay.setOrderId(new Long(out_trade_no));
			orderpay.setPayNo(trade_no);
			orderpay.setPayType("zfb");
			orderpay.setPayStatus("TRADE_SUCCESS");
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			orderpay.setPayTime(format.parse(time));
			orderService.updateOrderStatus(orderpay);

			//删除购物车
			//TODO


			// 返回订单号
			model.addAttribute("orderId", out_trade_no);

			return "paySuccess";
		} else {
			return "payError";
		}
	}

	// 支付宝
	private String zfb(Order order) {
		// 获得初始化的AlipayClient
		AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getGatewayUrl(), AlipayConfig.app_id,
				AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key,
				AlipayConfig.sign_type);

		// 设置请求参数
		AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
		alipayRequest.setReturnUrl(alipayConfig.getReturn_url());
		alipayRequest.setNotifyUrl(alipayConfig.getNotify_url());

		// 商户订单号，商户网站订单系统中唯一订单号，必填
		long out_trade_no = order.getId();
		// 付款金额，必填
		double total_amount = order.getFinalPayMoney();
		// 订单名称，必填
		String subject = "go8商城订单";
		// 商品描述，可空
		String body = order.getRemark();

		alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\"," + "\"total_amount\":\"" + total_amount
				+ "\"," + "\"subject\":\"" + subject + "\"," + "\"body\":\"" + body + "\","
				+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

		// 若想给BizContent增加其他可选请求参数，以增加自定义超时时间参数timeout_express来举例说明
		// alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
		// + "\"total_amount\":\""+ total_amount +"\","
		// + "\"subject\":\""+ subject +"\","
		// + "\"body\":\""+ body +"\","
		// + "\"timeout_express\":\"10m\","
		// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
		// 请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节

		// 生成支付宝请求的html页面
		String result;
		try {
			result = alipayClient.pageExecute(alipayRequest).getBody();
		} catch (AlipayApiException e) {
			e.printStackTrace();
			result = "支付跳转异常";
		}
		// 返回支付页面
		return result;
	}
}
